home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Graphics Programming (2nd Edition) / Visual Basic Graphics Programming 2nd Edition.iso / Src / Ch15 / Face.cls < prev    next >
Text File  |  1999-06-24  |  14KB  |  488 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "Face3d"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = False
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. Option Explicit
  15.  
  16. ' Point3D is defined in module M3OPS.BAS as:
  17. '    Type Point3D
  18. '        coord(1 To 4) As Single
  19. '        trans(1 To 4) As Single
  20. '    End Type
  21.  
  22. Public NumPts As Long       ' Number of points.
  23. Private Points() As Point3D ' Data points.
  24.  
  25. Public IsCulled As Boolean
  26.  
  27. Private Type POINTAPI
  28.     X As Long
  29.     Y As Long
  30. End Type
  31. Private Declare Function Polygon Lib "gdi32" (ByVal hdc As Long, lpPoint As POINTAPI, ByVal nCount As Long) As Long
  32.  
  33. ' Return True if this polygon partially obscures
  34. ' (has greater Z value than) polygon target.
  35. '
  36. ' We assume one polygon may obscure the other, but
  37. ' they cannot obscure each other.
  38. '
  39. ' This check is executed by seeing where the
  40. ' projections of the edges of the polygons cross.
  41. ' Where they cross, see if one Z value is greater
  42. ' than the other.
  43. '
  44. ' If no edges cross, see if one polygon contains
  45. ' the other. If so, there is an overlap.
  46. Public Function Obscures(ByVal target As Face3d) As Boolean
  47. Dim num As Integer
  48. Dim i As Integer
  49. Dim j As Integer
  50. Dim xi1 As Single
  51. Dim yi1 As Single
  52. Dim zi1 As Single
  53. Dim xi2 As Single
  54. Dim yi2 As Single
  55. Dim zi2 As Single
  56. Dim xj1 As Single
  57. Dim yj1 As Single
  58. Dim zj1 As Single
  59. Dim xj2 As Single
  60. Dim yj2 As Single
  61. Dim zj2 As Single
  62. Dim X As Single
  63. Dim Y As Single
  64. Dim z1 As Single
  65. Dim z2 As Single
  66.  
  67.     num = target.NumPts
  68.  
  69.     ' Check each edge in this polygon.
  70.     GetTransformedPoint NumPts, xi1, yi1, zi1
  71.     For i = 1 To NumPts
  72.         GetTransformedPoint i, xi2, yi2, zi2
  73.  
  74.         ' Compare with each edge in the other.
  75.         target.GetTransformedPoint num, xj1, yj1, zj1
  76.         For j = 1 To num
  77.             target.GetTransformedPoint j, xj2, yj2, zj2
  78.             ' See if the segments cross.
  79.             If FindCrossing( _
  80.                 xi1, yi1, zi1, _
  81.                 xi2, yi2, zi2, _
  82.                 xj1, yj1, zj1, _
  83.                 xj2, yj2, zj2, _
  84.                 X, Y, z1, z2) _
  85.             Then
  86.                 If z1 - z2 > 0.01 Then
  87.                     ' z1 > z2. We obscure it.
  88.                     Obscures = True
  89.                     Exit Function
  90.                 End If
  91.                 If z2 - z1 > 0.01 Then
  92.                     ' z2 > z1. It obscures us.
  93.                     Obscures = False
  94.                     Exit Function
  95.                 End If
  96.             End If
  97.  
  98.             xj1 = xj2
  99.             yj1 = yj2
  100.             zj1 = zj2
  101.         Next j
  102.  
  103.         xi1 = xi2
  104.         yi1 = yi2
  105.         zi1 = zi2
  106.     Next i
  107.     
  108.     ' No edges cross. See if one polygon contains
  109.     ' the other.
  110.  
  111.     ' If any points of one polygon are inside the
  112.     ' other, then they must all be. Since the
  113.     ' IsAbove tests were inconclusive, some points
  114.     ' in one polygon are on the "bad" side of the
  115.     ' other. In that case there is an overlap.
  116.  
  117.     ' See if this polygon is inside the other.
  118.     GetTransformedPoint 1, xi1, yi1, zi1
  119.     If target.PointInside(xi1, yi1) Then
  120.         Obscures = True
  121.         Exit Function
  122.     End If
  123.  
  124.     ' See if the other polygon is inside this one.
  125.     target.GetTransformedPoint 1, xi1, yi1, zi1
  126.     If PointInside(xi1, yi1) Then
  127.         Obscures = True
  128.         Exit Function
  129.     End If
  130.  
  131.     Obscures = False
  132. End Function
  133. ' See where the projections of two segments cross.
  134. ' Return true if the segments cross, false
  135. ' otherwise.
  136. Private Function FindCrossing( _
  137.     ByVal ax1 As Single, ByVal ay1 As Single, ByVal az1 As Single, _
  138.     ByVal ax2 As Single, ByVal ay2 As Single, ByVal az2 As Single, _
  139.     ByVal bx1 As Single, ByVal by1 As Single, ByVal bz1 As Single, _
  140.     ByVal bx2 As Single, ByVal by2 As Single, ByVal bz2 As Single, _
  141.     ByRef X As Single, ByRef Y As Single, ByRef z1 As Single, ByRef z2 As Single) _
  142.         As Boolean
  143. Dim dxa As Single
  144. Dim dya As Single
  145. Dim dza As Single
  146. Dim dxb As Single
  147. Dim dyb As Single
  148. Dim dzb As Single
  149. Dim t1 As Single
  150. Dim t2 As Single
  151. Dim denom As Single
  152.  
  153.     dxa = ax2 - ax1
  154.     dya = ay2 - ay1
  155.     dxb = bx2 - bx1
  156.     dyb = by2 - by1
  157.     
  158.     FindCrossing = False
  159.     
  160.     denom = dxb * dya - dyb * dxa
  161.     ' If the segments are parallel, stop.
  162.     If denom < 0.01 And denom > -0.01 Then Exit Function
  163.  
  164.     t2 = (ax1 * dya - ay1 * dxa - bx1 * dya + by1 * dxa) / denom
  165.     If t2 < 0 Or t2 > 1 Then Exit Function
  166.     
  167.     t1 = (ax1 * dyb - ay1 * dxb - bx1 * dyb + by1 * dxb) / denom
  168.     If t1 < 0 Or t1 > 1 Then Exit Function
  169.  
  170.     ' Compute the points of overlap.
  171.     X = ax1 + t1 * dxa
  172.     Y = ay1 + t1 * dya
  173.     dza = az2 - az1
  174.     dzb = bz2 - bz1
  175.     z1 = az1 + t1 * dza
  176.     z2 = bz1 + t2 * dzb
  177.     FindCrossing = True
  178. End Function
  179.  
  180. ' Return True if the point projection lies within
  181. ' this polygon's projection.
  182. Public Function PointInside(ByVal X As Single, ByVal Y As Single) As Boolean
  183. Dim i As Integer
  184. Dim theta1 As Double
  185. Dim theta2 As Double
  186. Dim dtheta As Double
  187. Dim dx As Double
  188. Dim dy As Double
  189. Dim angles As Double
  190.  
  191.     dx = Points(NumPts).trans(1) - X
  192.     dy = Points(NumPts).trans(2) - Y
  193.     theta1 = ATan2(CSng(dy), CSng(dx))
  194.     If theta1 < 0 Then theta1 = theta1 + 2 * PI
  195.     For i = 1 To NumPts
  196.         dx = Points(i).trans(1) - X
  197.         dy = Points(i).trans(2) - Y
  198.         theta2 = ATan2(CSng(dy), CSng(dx))
  199.         If theta2 < 0 Then theta2 = theta2 + 2 * PI
  200.         dtheta = theta2 - theta1
  201.         If dtheta > PI Then dtheta = dtheta - 2 * PI
  202.         If dtheta < -PI Then dtheta = dtheta + 2 * PI
  203.         angles = angles + dtheta
  204.         theta1 = theta2
  205.     Next i
  206.  
  207.     PointInside = (Abs(angles) > 0.001)
  208. End Function
  209.  
  210. ' Return True if this polygon is completly above
  211. ' the plane containing target.
  212. Public Function IsAbove(ByVal target As Face3d) As Boolean
  213. Dim nx As Single
  214. Dim ny As Single
  215. Dim nz As Single
  216. Dim px As Single
  217. Dim py As Single
  218. Dim pz As Single
  219. Dim dx As Single
  220. Dim dy As Single
  221. Dim dz As Single
  222. Dim cx As Single
  223. Dim cy As Single
  224. Dim cz As Single
  225. Dim i As Integer
  226.  
  227.     ' Compute an upward pointing normal to the plane.
  228.     target.TransformedNormalVector nx, ny, nz
  229.     If nz < 0 Then
  230.         nx = -nx
  231.         ny = -ny
  232.         nz = -nz
  233.     End If
  234.  
  235.     ' Get a point on the plane.
  236.     target.GetTransformedPoint 1, px, py, pz
  237.  
  238.     ' See if the points in this polygon all lie
  239.     ' above the plane containing target.
  240.     For i = 1 To NumPts
  241.         ' Get the vector from plane to point.
  242.         dx = Points(i).trans(1) - px
  243.         dy = Points(i).trans(2) - py
  244.         dz = Points(i).trans(3) - pz
  245.  
  246.         ' If the dot product < 0, the point is
  247.         ' below the plane.
  248.         If dx * nx + dy * ny + dz * nz < -0.01 Then
  249.             IsAbove = False
  250.             Exit Function
  251.         End If
  252.     Next i
  253.     IsAbove = True
  254. End Function
  255. ' Return true if this polygon is completly below
  256. ' the plane containing target.
  257. Public Function IsBelow(ByVal target As Face3d) As Boolean
  258. Dim nx As Single
  259. Dim ny As Single
  260. Dim nz As Single
  261. Dim px As Single
  262. Dim py As Single
  263. Dim pz As Single
  264. Dim dx As Single
  265. Dim dy As Single
  266. Dim dz As Single
  267. Dim cx As Single
  268. Dim cy As Single
  269. Dim cz As Single
  270. Dim i As Integer
  271.  
  272.     ' Compute a downward pointing normal to the plane.
  273.     target.TransformedNormalVector nx, ny, nz
  274.     If nz > 0 Then
  275.         nx = -nx
  276.         ny = -ny
  277.         nz = -nz
  278.     End If
  279.  
  280.     ' Get a point on the plane.
  281.     target.GetTransformedPoint 1, px, py, pz
  282.  
  283.     ' See if the points in this polygon all lie
  284.     ' below the plane containing target.
  285.     For i = 1 To NumPts
  286.         ' Get the vector from plane to point.
  287.         dx = Points(i).trans(1) - px
  288.         dy = Points(i).trans(2) - py
  289.         dz = Points(i).trans(3) - pz
  290.  
  291.         ' If the dot product < 0, the point is
  292.         ' below the plane.
  293.         If dx * nx + dy * ny + dz * nz < -0.01 Then
  294.             IsBelow = False
  295.             Exit Function
  296.         End If
  297.     Next i
  298.     IsBelow = True
  299. End Function
  300. ' Return the transformed coordinates of a point
  301. ' on the polygon.
  302. Public Sub GetTransformedPoint(ByVal Index As Long, ByRef X As Single, ByRef Y As Single, ByRef z As Single)
  303.     X = Points(Index).trans(1)
  304.     Y = Points(Index).trans(2)
  305.     z = Points(Index).trans(3)
  306. End Sub
  307. ' Return the bounds of this polygon.
  308. Public Sub GetExtent(ByRef Xmin As Single, ByRef xmax As Single, ByRef ymin As Single, ByRef ymax As Single, ByRef zmin As Single, ByRef zmax As Single)
  309. Dim i As Integer
  310.  
  311.     If NumPts < 1 Then Exit Sub
  312.  
  313.     With Points(1)
  314.         Xmin = .trans(1)
  315.         xmax = Xmin
  316.         ymin = .trans(2)
  317.         ymax = ymin
  318.         zmin = .trans(3)
  319.         zmax = zmin
  320.     End With
  321.  
  322.     For i = 2 To NumPts
  323.         With Points(i)
  324.             If Xmin > .trans(1) Then Xmin = .trans(1)
  325.             If xmax < .trans(1) Then xmax = .trans(1)
  326.             If ymin > .trans(2) Then ymin = .trans(2)
  327.             If ymax < .trans(2) Then ymax = .trans(2)
  328.             If zmin > .trans(3) Then zmin = .trans(3)
  329.             If zmax < .trans(3) Then zmax = .trans(3)
  330.         End With
  331.     Next i
  332. End Sub
  333.  
  334.  
  335. ' Compute a normal vector for this polygon.
  336. Public Sub NormalVector(ByRef nx As Single, ByRef ny As Single, ByRef nz As Single)
  337. Dim Ax As Single
  338. Dim Ay As Single
  339. Dim Az As Single
  340. Dim Bx As Single
  341. Dim By As Single
  342. Dim Bz As Single
  343.  
  344.     Ax = Points(2).coord(1) - Points(1).coord(1)
  345.     Ay = Points(2).coord(2) - Points(1).coord(2)
  346.     Az = Points(2).coord(3) - Points(1).coord(3)
  347.     Bx = Points(3).coord(1) - Points(2).coord(1)
  348.     By = Points(3).coord(2) - Points(2).coord(2)
  349.     Bz = Points(3).coord(3) - Points(2).coord(3)
  350.     m3Cross nx, ny, nz, Ax, Ay, Az, Bx, By, Bz
  351. End Sub
  352. ' Compute a transformed normal vector for this polygon.
  353. Public Sub TransformedNormalVector(ByRef nx As Single, ByRef ny As Single, ByRef nz As Single)
  354. Dim Ax As Single
  355. Dim Ay As Single
  356. Dim Az As Single
  357. Dim Bx As Single
  358. Dim By As Single
  359. Dim Bz As Single
  360.  
  361.     Ax = Points(2).trans(1) - Points(1).trans(1)
  362.     Ay = Points(2).trans(2) - Points(1).trans(2)
  363.     Az = Points(2).trans(3) - Points(1).trans(3)
  364.     Bx = Points(3).trans(1) - Points(2).trans(1)
  365.     By = Points(3).trans(2) - Points(2).trans(2)
  366.     Bz = Points(3).trans(3) - Points(2).trans(3)
  367.     m3Cross nx, ny, nz, Ax, Ay, Az, Bx, By, Bz
  368. End Sub
  369.  
  370.  
  371. ' Add one or more points to the polygon.
  372. Public Sub AddPoints(ParamArray coord() As Variant)
  373. Dim num_pts As Integer
  374. Dim i As Integer
  375. Dim pt As Integer
  376.  
  377.     num_pts = (UBound(coord) + 1) \ 3
  378.     ReDim Preserve Points(1 To NumPts + num_pts)
  379.  
  380.     pt = 0
  381.     For i = 1 To num_pts
  382.         Points(NumPts + i).coord(1) = coord(pt)
  383.         Points(NumPts + i).coord(2) = coord(pt + 1)
  384.         Points(NumPts + i).coord(3) = coord(pt + 2)
  385.         Points(NumPts + i).coord(4) = 1#
  386.         pt = pt + 3
  387.     Next i
  388.  
  389.     NumPts = NumPts + num_pts
  390. End Sub
  391. ' Apply a transformation matrix which may not
  392. ' contain 0, 0, 0, 1 in the last column to the
  393. ' object.
  394. Public Sub ApplyFull(M() As Single)
  395. Dim i As Integer
  396.  
  397.     ' Do nothing if we are culled.
  398.     If IsCulled Then Exit Sub
  399.  
  400.     For i = 1 To NumPts
  401.         m3ApplyFull Points(i).coord, M, Points(i).trans
  402.     Next i
  403. End Sub
  404.  
  405. ' Apply a transformation matrix to the object.
  406. Public Sub Apply(M() As Single)
  407. Dim i As Integer
  408.  
  409.     ' Do nothing if we are culled.
  410.     If IsCulled Then Exit Sub
  411.  
  412.     For i = 1 To NumPts
  413.         m3Apply Points(i).coord, M, Points(i).trans
  414.     Next i
  415. End Sub
  416.  
  417. ' Draw the transformed points on a Form, Printer,
  418. ' or PictureBox.
  419. Public Sub Draw(ByVal pic As PictureBox, Optional r As Variant)
  420. Dim pts() As POINTAPI
  421. Dim i As Integer
  422.  
  423.     ' Do nothing if we are culled.
  424.     If IsCulled Then Exit Sub
  425.     If NumPts < 3 Then Exit Sub
  426.  
  427.     ReDim pts(1 To NumPts)
  428.     For i = 1 To NumPts
  429.         pts(i).X = Points(i).trans(1)
  430.         pts(i).Y = Points(i).trans(2)
  431.     Next i
  432.  
  433.     Polygon pic.hdc, pts(1), NumPts
  434. End Sub
  435. ' Cull if any points are behind the center of
  436. ' projection.
  437. Public Sub ClipEye(ByVal r As Single)
  438. Dim pt As Integer
  439.  
  440.     ' Do nothing if we are already culled.
  441.     If IsCulled Then Exit Sub
  442.  
  443.     For pt = 1 To NumPts
  444.         If Points(pt).trans(3) >= r Then Exit For
  445.     Next pt
  446.  
  447.     If pt <= NumPts Then IsCulled = True
  448. End Sub
  449. ' Perform backface removal for the center
  450. ' of projection (X, Y, Z).
  451. Public Sub Cull(ByVal X As Single, ByVal Y As Single, ByVal z As Single)
  452. Dim Ax As Single
  453. Dim Ay As Single
  454. Dim Az As Single
  455. Dim nx As Single
  456. Dim ny As Single
  457. Dim nz As Single
  458.  
  459.     ' Compute a normal to the face.
  460.     NormalVector nx, ny, nz
  461.  
  462.     ' Compute a vector from the center of
  463.     ' projection to the face.
  464.     Ax = Points(1).coord(1) - X
  465.     Ay = Points(1).coord(2) - Y
  466.     Az = Points(1).coord(3) - z
  467.  
  468.     ' See if the vectors meet at an angle < 90.
  469.     IsCulled = (Ax * nx + Ay * ny + Az * nz > -0.0001)
  470. End Sub
  471. ' Return the largest transformed Z value for this face.
  472. Public Function zmax() As Single
  473. Dim i As Integer
  474. Dim z_max As Single
  475.  
  476.     z_max = -1E+30
  477.     If IsCulled Then Exit Function
  478.  
  479.     For i = 1 To NumPts
  480.         If z_max < Points(i).trans(3) _
  481.             Then z_max = Points(i).trans(3)
  482.     Next i
  483.  
  484.     zmax = z_max
  485. End Function
  486.  
  487.  
  488.